Skip to content

chore(release): v9.14.0 RC - #13822

Closed
artsyit wants to merge 4 commits into
mainfrom
rc-v9.14.0
Closed

chore(release): v9.14.0 RC#13822
artsyit wants to merge 4 commits into
mainfrom
rc-v9.14.0

Conversation

@artsyit

@artsyit artsyit commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Release Candidate 9.14.0

This branch was automatically created as the release candidate for version 9.14.0.

⚠️ Do not merge this PR. It is used for tracking the release and 🍒 cherry-picking fixes only.

Changelog

Cross-platform user-facing changes

iOS user-facing changes

Dev changes

#nochangelog

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

🎉 Beta Versions Generated (commit: 10fdffe)

Android 🤖

  • 9.14.0 (2026072410) - Available on Firebase
  • 9.14.0 (2026072410) - Available on Play Store

iOS 🍏

  • 9.14.0 (2026.07.24.10) - Available on Firebase
  • 9.14.0 (2026.07.24.10) - Available on TestFlight

@MrSltun MrSltun self-assigned this Jul 24, 2026
@github-actions github-actions Bot deleted a comment from claude Bot Jul 27, 2026
@github-actions github-actions Bot deleted a comment from claude Bot Jul 27, 2026
Comment thread src/app/Components/Countdown/Ticker.tsx
* fix: rail tracking

* fix: items impression tracking

* remove unnecessary test

* track all live sections after pull to refresh when in view

* chore

* chore: edit the way the nwfy view more cta is rendered

* chore: revert new santinel placement

* chore: tests

* chore: revert chore: edit the way the nwfy view more cta is rendered

---------

Co-authored-by: dariakoko <[email protected]>
@github-actions github-actions Bot deleted a comment from claude Bot Jul 28, 2026
@artsyit

artsyit commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author
Warnings
⚠️

No changelog changes

Generated by 🚫 dangerJS against 9fa3fa1

Comment on lines +112 to +118
useEffect(() => {
if (hasPendingRailViewed && isRailInViewport) {
tracking.viewedSection(contextModule, index)
setHasPendingRailViewed(false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [hasPendingRailViewed, isRailInViewport])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things about this effect:

1. Duplicate railViewed on first scroll-into-view. HomeViewSectionSentinel (rendered at line 216) also fires viewedSection the first time the section becomes visible — its only guard is trackedSections (HomeViewSectionSentinel.tsx:27), which is per-contextModule, not per-refresh. Scenario:

  1. Cold home view, the recommended/NWFY rail is below the fold → sentinel hasn't fired, viewableSections doesn't include the section.
  2. User navigates away and back → useFocusEffect in HomeView.tsx:172 bumps liveRefetchKeyhasPendingRailViewed = true.
  3. User scrolls the rail into view → the outer FlashList sets viewableSections and the sentinel flips visible.

Both paths now call tracking.viewedSection(contextModule, index) → two railViewed events for the same rail in the same moment.

2. railViewed fires before the refreshed data lands. setHasPendingRailViewed(true) runs synchronously on the bump, so when the rail is already on screen this effect fires in the same commit — before fetchQuery resolves, and it fires even if the fetch errors. The old code fired from complete. If that's the intended behaviour, worth saying so in the comment, since "re-fire railViewed after a live refresh" reads as "after the refresh landed".

Comment on lines +195 to +196
// We are using this key to force a re-render to track item impressions again
key={trackingKey}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "force a re-render", but a changing key forces a full unmount + remount of HomeViewSectionArtworksGrid, which throws away its local state. That resets hasGridLaidOut to false (HomeViewSectionArtworksGrid.tsx:34), so the "View More" button unmounts and only comes back after the async measureInWindow callback lands (HomeViewSectionArtworksGrid.tsx:40-46). Every live refresh of a grid section flashes that CTA out and back in, and remounts all ArtworkGridItems with it.

The useLayoutEffect already re-measures on [artworks], so the remount isn't needed for layout — it's only there to clear the grid's own trackedGridItems ref (line 38), which the parent's resetTracking() doesn't touch. Clearing that set directly is cheaper and avoids the flash, e.g. pass trackingKey down and:

useEffect(() => {
  trackedGridItems.clear()
}, [trackingKey])

Comment on lines 115 to +118
const resetTracking = useCallback(() => {
trackedItems.clear()
}, [trackedItems])
setKey(key + 1)
}, [trackedItems, key])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setKey(key + 1) reads key from the closure, and resetTracking is invoked from a long-lived async callback (fetchQuery(...).subscribe({ complete }) in HomeViewSectionArtworks.tsx:95) whose enclosing effect deps are just [liveRefetchKey]. It happens to work today because the effect re-runs on every bump, but a functional update removes the staleness question and stops resetTracking's identity from churning on every reset:

Suggested change
const resetTracking = useCallback(() => {
trackedItems.clear()
}, [trackedItems])
setKey(key + 1)
}, [trackedItems, key])
const resetTracking = useCallback(() => {
trackedItems.clear()
setKey((prev) => prev + 1)
}, [trackedItems])

}
},
[enableItemsViewsTracking, isInViewport, contextScreenOwnerType, contextModule]
[enableItemsViewsTracking, isInViewport, contextScreenOwnerType, contextModule, key]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

key is added to the useAnimatedReaction deps (production path) but not to the __TEST__ effect at lines 89-100. Nothing in that dep list changes when key bumps — trackItems deps (lines 57-65) don't include key either — so in tests a resetTracking() never re-triggers trackItems on its own.

That means the behaviour this dep buys is untested: HomeViewSectionArtworks.tests.tsx:512 ("re-enables itemViewed tracking after the live refresh completes") passes only because the test manually re-invokes onViewableItemsChanged after the refresh. The real case for the rail path — rail stays exactly in place after returning to home, so viewable items never change — is what the key dep is for and it isn't covered. Adding key to the test effect's deps would make the two paths behave the same and let a test assert it.

@claude

claude Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Code Review

Summary

Release-candidate branch for 9.14.0 carrying three cherry-picks on top of the RC open commit: floor fractional seconds in the countdown ticker (#13830), a copy change on the edit-profile message (#13832), and a rework of home-view live-rail tracking (#13823). The ticker and copy changes are self-contained; all of my comments are on the tracking rework.

Issues Found

🟡 Duplicate railViewed when an off-screen live rail is first scrolled into view after a refreshHomeViewSectionArtworks.tsx:112-118

HomeViewSectionSentinel (rendered at line 216) also fires viewedSection when the section becomes visible, guarded only by trackedSections per contextModule (HomeViewSectionSentinel.tsx:27). Path:

  1. Cold home view, rail below the fold — sentinel hasn't fired, section not in viewableSections.
  2. Navigate away and back → HomeView.tsx:172 bumps liveRefetchKeyhasPendingRailViewed = true.
  3. Scroll the rail into view → the outer FlashList populates viewableSections and the sentinel flips visible.

Both call tracking.viewedSection(contextModule, index), so two railViewed events land for the same rail. The test at HomeViewSectionArtworks.tests.tsx:465-478 asserts toHaveBeenCalledWith rather than a call count, so it wouldn't catch this.

🟡 key={trackingKey} remounts the grid and flashes the "View More" CTAHomeViewSectionArtworks.tsx:195-196

The comment says "force a re-render", but a changing key unmounts and remounts HomeViewSectionArtworksGrid, dropping its local state. hasGridLaidOut resets to false (HomeViewSectionArtworksGrid.tsx:34) and the button only reappears after the async measureInWindow callback (lines 40-46). Every live refresh of a grid section flashes the CTA and remounts all ArtworkGridItems.

The useLayoutEffect already re-measures on [artworks], so the remount isn't needed for layout — its only real job is clearing the grid's own trackedGridItems ref (line 38), which the parent's resetTracking() doesn't touch. Passing trackingKey down and clearing that set in an effect gets the same reset without the flash.

🟡 railViewed now fires before the refreshed data landsHomeViewSectionArtworks.tsx:85-118

setHasPendingRailViewed(true) runs synchronously on the bump, so when the rail is on screen the event fires in the same commit — before fetchQuery resolves, and also when the fetch errors (only console.error on that branch). Previously it fired from complete. If that's deliberate, say so in the comment; "re-fire railViewed after a live refresh" currently reads as "after the refresh landed".

🟡 The key dep is only wired into the production path, so the behaviour it buys is untesteduseImpressionsTracking.ts:110

key is in the useAnimatedReaction deps but not in the __TEST__ effect at lines 89-100, and trackItems (lines 57-65) doesn't depend on it either. So under __TEST__ a resetTracking() never re-triggers trackItems by itself. HomeViewSectionArtworks.tests.tsx:512 passes only because it manually re-invokes onViewableItemsChanged. The case the dep exists for — rail stays exactly in place after returning to home, so viewable items never change — has no coverage.

🟢 Use a functional state updateuseImpressionsTracking.ts:117

setKey(key + 1) reads key from a closure that's invoked from a long-lived fetchQuery(...).subscribe({ complete }) callback whose effect deps are [liveRefetchKey]. It works today because the effect re-runs on each bump, but setKey((prev) => prev + 1) removes the staleness question and stops resetTracking's identity from changing on every reset.

Areas Reviewed

Ticker (#13830) — Correct. duration.shiftTo(...) puts the remainder in the smallest unit, so only d.seconds was ever fractional; the extra Math.floor on days/hours/minutes is harmless. ModernTicker (Ticker.tsx:105-111) passes unfloored strings, but getTimerInfo runs them through parseInt (saleTime.ts:33-36), so it was never affected. The new test covers the regression.

Copy change (#13832) — No concerns.

Performance — Covered above: the grid remount is the only regression I found.

Security — Nothing relevant.

Questions for Author

  • Is firing railViewed on bump rather than on complete intentional? The renamed test ("as soon as the rail is refreshed") suggests yes, but it means the event can precede, or outlive, a failed refresh.
  • Are the live sections (home-view-section-recommended-artworks, home-view-section-new-works-for-you) ever served with component.type === "ArtworksGrid"? The key={trackingKey} change only affects the grid branch, and the live-refresh tests all exercise the ArtworkRail branch, so that path has no coverage.

Note on this PR

This is the RC tracking PR ("do not merge"), so each cherry-pick was reviewed on its own PR — the screenshot/recording and LLM-disclosure conventions apply there, not here. #13830's commit does carry Assisted-by: Claude Sonnet 5. The findings above are worth raising because they ship in 9.14.0; they are fine as follow-ups on main rather than cherry-picks if none are release-blocking.

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

🎉 Beta Versions Generated (commit: 9fa3fa1)

iOS 🍏

  • 9.14.0 (2026.07.28.15) - Available on Firebase
  • 9.14.0 (2026.07.28.15) - Available on TestFlight

Android 🤖

  • 9.14.0 (2026072815) - Available on Firebase
  • 9.14.0 (2026072815) - Available on Play Store

@MrSltun MrSltun closed this Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants